using Box2DSharp.Testbed.Unity.Inspection;
using Box2DSharp.Testbed.Unity;
using LitJson;
using System.Collections;
using System.Collections.Generic;
using Testbed.Abstractions;
using Testbed.TestCases;
using UnityEngine;
using Box2DSharp.Common;
using System.Reflection;
using System.Threading;
using Box2DSharp.Dynamics;
using UnityEngine.InputSystem;
using UnityEngine.EventSystems;
using cframework.lockstep;

using static UnityEngine.EventSystems.EventTrigger;



//发送 >>>{ "msgID":"login.1", "data":{ "uid":"u162719"}}
//接收 <<<{ "msgID":"login.1", "data":{ "code":0} }

//发送 >>>{ "msgID":"createRoom.2"}
//接收 <<<{ "msgID":"createRoom.2","data":{ "code":0,"roomID":5} }

//发送 >>>{ "msgID":"joinRoom.3","data":{ "roomID":5}}
//接收 <<<{ "msgID":"joinRoom.3","data":{ "code":0} }

//发送 >>>{ "msgID":"startGame.4"}
//接收 <<<{ "msgID":"startGame.4","data":{ "code":0} } // add all player

//接收 <<<{ "msgID":"frame","data":{ "players":[],"index":0} }
//接收 <<<{ "msgID":"frame","data":{ "players":[],"index":1} }


public class CannonGame : MonoBehaviour
{
    public int serverHerz = 15;
    public int clientHerz = 30;

    CannonWorld test;

    LockStepWorld fuw = new LockStepWorld();
    
    MsgQueue<object> msgQueue = new MsgQueue<object>(100);


    bool mouseLeftBtnPress = false;
    Vector3 mouseStartPos = Vector2.zero;
    float joystickAngle = -1;
    bool rightMouseBtn = false;
    public UnityEngine.UI.Image joystick;
    public UnityEngine.UI.Image joystick2;




    string log;
    string log2 = "";
    string roomID;
    string uid;

    Dictionary<long, double> msgSentTimes = new Dictionary<long, double>();



    
    long msgDuringEnqueueTime;

    private double msgSendDealTime;
    private double sumMsgSendDealTime;
    MsgQueue<double> msgSendDealTimes = new MsgQueue<double>(30);

    NetManager netManager = new NetManager();


    void Start()
    {
        WebSocketHelper.netManager = netManager;

        fuw.clientHerz = clientHerz;
        fuw.serverHerz = serverHerz;
        fuw.serverMsgBeginIndex = 0;
        fuw.processClientStepCallback = ProcessClientStep;
        fuw.processServerStepCallback = ProcessServerStep;
        fuw.processWaitMsgStepCallback = ProcessWaitMsgStep;

        QualitySettings.vSyncCount = 0;
        Application.targetFrameRate = 60;
        Time.fixedDeltaTime = 1f / 500;

        test = new CannonWorld();

        joystick2.gameObject.SetActive(false);
        joystick.gameObject.SetActive(false);

        FP a = 3.4f;

        var Settings = TestSettingHelper.Load();
        Settings.Hertz = this.fuw.clientHerz;
        Global.Settings = Settings;
        Global.Camera.Width = Settings.WindowWidth;
        Global.Camera.Height = Settings.WindowHeight;

        var unityDrawer = UnityDrawer.GetDrawer();
        var DebugDrawer = new DebugDrawer { Drawer = unityDrawer, ShowUI = true };
        Global.DebugDrawer = DebugDrawer;


        test.Input = Global.Input;
        test.Drawer = Global.DebugDrawer;
        test.TestSettings = Global.Settings;
        test.World.Drawer = Global.DebugDrawer;
        test.TextIncrement = 20;


        //var server = GameObject.FindObjectOfType<WebSocketServer.WebSocketServer>();



        uid = "u" + System.DateTime.Now.ToLocalTime().ToString("HHmmss");
        //API.onFrame = (JsonData data) =>
        //{
        //    //log = data.ToJson();            



        //    //lock(this)
        //    //{
        //    //    frameMsgQueue.Enqueue(data);
        //    //    msgEqueueTimes.Add(data, System.DateTime.Now.Ticks);
        //    //    frameMsgQueueCount = frameMsgQueue.Count;
        //    //}



        //    //while (frameMsgArr[writeIndex] != null)
        //    //{
        //    //    continue;
        //    //}



        //    //msgEqueueTimes[writeIndex] = System.DateTime.Now.Ticks;
        //    //frameMsgArr[writeIndex] = data;
        //    //writeIndex =(writeIndex + 1) % frameMsgArr.Length;

        //    while(!msgQueue.Enqueue(new KeyValuePair<object, long> (data, System.DateTime.Now.Ticks)))
        //    {
        //        continue;
        //    }

        //};

        netManager.onMessage = (json) =>
        {
            string msgID = (string)json["msgID"];

            if (msgID == "frame")
            { 
                while (!msgQueue.Enqueue(new KeyValuePair<object, long>(json["data"], System.DateTime.Now.Ticks)))
                {
                    continue;
                }
            }

            //lock (msgQueue)
            //{
            //    msgQueue.Enqueue(data);
            //}
        };
    }

    private void OnDestroy()
    {
        WebSocketHelper.I.CloseClientWebSocket();
    }

    int updateCount = 0;
    int updateCountHerz;
    double updateCountStartTime;
    double lastTime;

    protected void FixedUpdate()
    {


        if(updateCount == 0)
        {
            updateCountStartTime = Time.realtimeSinceStartupAsDouble;
        }
        updateCount++;

        //Debug.LogError($"test: {updateCount}: " + Time.realtimeSinceStartupAsDouble + " | " + (Time.realtimeSinceStartupAsDouble - lastTime).ToString("F6"));
        lastTime = Time.realtimeSinceStartupAsDouble;

        if (Time.realtimeSinceStartupAsDouble - updateCountStartTime >= 1)
        {
            updateCountHerz = updateCount;
            updateCount = 0;
        }

        //return;

        //lock (msgQueue)
        //{
        //    while (msgQueue.Count > 0)
        //    {
        //        var data = msgQueue.Dequeue() as JsonData;
        //        if (data["msgID"].asString() == "startGame.4")
        //        {
        //            Debug.LogError("test: start game");
        //        }
        //    }
        //}

        //if(frameMsgQueueCount > 0)
        //{
        //    lock (this)
        //    {
        //        var temp = frameMsgQueue;
        //        frameMsgQueue = frameMsgQueueTemp;
        //        frameMsgQueueTemp = temp;

        //        frameMsgQueueCount = frameMsgQueue.Count;
        //    }

        //    while (frameMsgQueueTemp.Count > 0)
        //    {
        //        var data = frameMsgQueueTemp.Dequeue() as JsonData;

        //        long msgEnqueueTime = 0;
        //        if(msgEqueueTimes.TryGetValue(data, out msgEnqueueTime))
        //        {
        //            msgEqueueTimes.Remove(data);
        //            msgDuringEnqueueTime = System.DateTime.Now.Ticks - msgEnqueueTime;
        //        }

        //        int index = int.Parse(data["index"].ToString());

        //        if (index == 0)
        //        {
        //            this.StartWorld(this.clientHerz, this.serverHerz, this.serverMsgBeginIndex);
        //        }

        //        if (!this.AddMsg(index, data))
        //        {
        //            Debug.LogError("add msg failed: " + index);
        //        }
        //    }
        //}


        //while (frameMsgArr[readIndex] != null)
        //{
        //    var data = frameMsgArr[readIndex] as JsonData;
        //    var msgEnqueueTime = msgEqueueTimes[readIndex];
        //    frameMsgArr[readIndex] = null;
        //    readIndex = (readIndex + 1) % frameMsgArr.Length;

        while (true)
        {
            var t = msgQueue.Dequeue();
            if(t == null)
            {
                break;
            }

            var dataObj = (KeyValuePair<object, long>)t;
            long msgEnqueueTime = dataObj.Value;

            msgDuringEnqueueTime = System.DateTime.Now.Ticks - msgEnqueueTime;
            var data = dataObj.Key as JsonData;

            int index = int.Parse(data["index"].ToString());

            if (index == 0)
            {
                this.fuw.StartWorld();
                this.test.uid = this.uid;
            }

            if (!this.fuw.AddMsg(index, data))
            {
                Debug.LogError("add msg failed: " + index);
            }
        }


        this.fuw.UpdateStep();
    }

    private void Update()
    {
        //if(EventSystem.current.IsPointerOverGameObject())
        {
            float tempJoystickAngle = joystickAngle;
            bool tempRightMouseBtn = false;
            if (Application.isMobilePlatform && !Application.isEditor)
            {
                if (Input.touchCount > 0)
                {
                    if (!mouseLeftBtnPress)
                    {
                        mouseLeftBtnPress = true;
                        mouseStartPos = Input.touches[0].position;

                        joystick.transform.position = mouseStartPos;
                        joystick.gameObject.SetActive(true);
                        joystick2.transform.position = mouseStartPos;
                        joystick2.gameObject.SetActive(true);
                    }
                    else
                    {
                        Vector3 touchPos = Input.touches[0].position;


                        var diffPos2 = touchPos - joystick.transform.position;
                        var r = joystick.transform as RectTransform;
                        if (diffPos2.magnitude > r.sizeDelta.x / 2)
                        {
                            joystick.transform.position = touchPos - r.sizeDelta.x / 2 * (touchPos - joystick.transform.position).normalized;
                            joystick2.transform.position = diffPos2.normalized * r.sizeDelta.x / 2 + joystick.transform.position;
                            mouseStartPos = joystick.transform.position;
                            mouseStartPos.z = 0;
                        }
                        else
                        {
                            joystick2.transform.position = touchPos;
                        }

                        var diffPos = touchPos - mouseStartPos;

                        if (diffPos.magnitude > 5)
                        {
                            tempJoystickAngle = Vector3.SignedAngle(Vector3.right, diffPos, Vector3.forward);
                            tempJoystickAngle += 360 * Mathf.CeilToInt(-Mathf.Min(0, tempJoystickAngle) / 360);
                        }
                        else
                        {
                            tempJoystickAngle = -1;
                        }
                    }
                }
                else
                {
                    joystick.gameObject.SetActive(false);
                    joystick2.gameObject.SetActive(false);
                    mouseLeftBtnPress = false;
                    tempJoystickAngle = -1;
                }

                tempRightMouseBtn = Input.touchCount > 1;
            }
            else
            {
                if (Input.GetMouseButtonDown(0))
                {
                    mouseLeftBtnPress = true;
                    mouseStartPos = Input.mousePosition;
                    joystick.transform.position = mouseStartPos;
                    joystick.gameObject.SetActive(true);
                    joystick2.transform.position = mouseStartPos;
                    joystick2.gameObject.SetActive(true);
                }
                else if (Input.GetMouseButton(0))
                {
                    if (mouseLeftBtnPress)
                    {
                        var diffPos2 = Input.mousePosition - joystick.transform.position;
                        var r = joystick.transform as RectTransform;
                        if (diffPos2.magnitude > r.sizeDelta.x / 2)
                        {
                            joystick.transform.position = Input.mousePosition - r.sizeDelta.x / 2 * (Input.mousePosition - joystick.transform.position).normalized;
                            joystick2.transform.position = diffPos2.normalized * r.sizeDelta.x / 2 + joystick.transform.position;
                            mouseStartPos = joystick.transform.position;
                            mouseStartPos.z = 0;
                        }
                        else
                        {
                            joystick2.transform.position = Input.mousePosition;
                        }

                        var diffPos = Input.mousePosition - mouseStartPos;

                        if (diffPos.magnitude > 5)
                        {
                            tempJoystickAngle = Vector3.SignedAngle(Vector3.right, diffPos, Vector3.forward);
                            tempJoystickAngle += 360 * Mathf.CeilToInt(-Mathf.Min(0, tempJoystickAngle) / 360);
                        }
                        else
                        {
                            tempJoystickAngle = -1;
                        }
                    }
                }
                else if (Input.GetMouseButtonUp(0))
                {
                    joystick.gameObject.SetActive(false);
                    joystick2.gameObject.SetActive(false);

                    mouseLeftBtnPress = false;
                    tempJoystickAngle = -1;
                }

                tempRightMouseBtn = Input.GetMouseButton(1);
            }

            if (tempJoystickAngle != joystickAngle || rightMouseBtn != tempRightMouseBtn)
            {
                joystickAngle = tempJoystickAngle;
                rightMouseBtn = tempRightMouseBtn;

                //List<int> ctrls = new List<int>();

                //ctrls.Add((int)(joystickAngle * 1000));
                //API.sendCtrl(ctrls);

                List<long> ctrls = new List<long>();

                var v = ((FP)(joystickAngle)).RawValue << 1;
                v |= rightMouseBtn ? 1L : 0L;


                if (!msgSentTimes.ContainsKey(v))
                {
                    msgSentTimes.Add(v, Time.realtimeSinceStartupAsDouble);
                }

                ctrls.Add(v);
                netManager.sendLongCtrl(ctrls);
            }

        }
    }

    protected void ProcessWaitMsgStep(float clientTime, float deltaTime)
    {
        //test.UpdateUserGameObject(clientTime, deltaTime);
    }

    protected void ProcessServerStep(int _serverFrameIndex, FP serverTime, FP deltaTime, object msgObj)
    {
        //Debug.LogError("ProcessServerStep: " + _serverFrameIndex + " | " + serverTime + " | " + deltaTime + " | " + Time.realtimeSinceStartup  + " | " + msgObj.ToString());

        if(msgObj != null)
        {
            var json = msgObj as JsonData;
            var players = json["players"] as JsonData;

            for(int i = 0; i < players.Count; i++)
            {
                var player = players[i];
                var uid = (string)player["uid"];
                var ctrls = player["ctrls"] as JsonData;



                test.CreateBody(uid);

                if(ctrls.Count > 0)
                {
                    var v = long.Parse((string)ctrls[0]);

                    double msgSentTime = 0;
                    if (msgSentTimes.TryGetValue(v, out msgSentTime))
                    {
                        msgSentTimes.Remove(v);

                        msgSendDealTime = Time.realtimeSinceStartupAsDouble - msgSentTime;


                        if(msgSendDealTimes.Length() >= msgSendDealTimes.Capility)
                        {
                            sumMsgSendDealTime -= msgSendDealTimes.Dequeue();
                        }

                        msgSendDealTimes.Enqueue(msgSendDealTime);
                        sumMsgSendDealTime += msgSendDealTime;
                    }

                    FP angle = FP.FromRaw(v >> 1);
                    bool btn = (v & 1) != 0;

                    if(btn)
                    {
                        test.userBtnIsPressed[uid] = btn;                        
                    }
                    else
                    {
                        test.userBtnIsPressed[uid] = btn;
                        

                    }
                    test.userStickjoyAngles[uid] = angle;
                }
            }
        }
    }

    protected void ProcessClientStep(int _clientFrameIndex, FP clientTime, FP deltaTime)
    {
        if(clientTime == 0)
        {
            this.test.CreateBody("test");
        }

        test.UpdateBodyAngleVelocity(clientTime);

        test.Step();
    }


    private void OnPreRender()
    {
        if (test.TestSettings == null)
        {
            return;
        }

        test.UpdateTransforms();
        test.Render();
    }



    void OnGUI()
    {
        GUILayout.BeginHorizontal();

        if(GUILayout.Button("连接"))
        {
            var address = GameObject.FindObjectOfType<WebSocketServer.WebSocketServer>().address;
            var port = GameObject.FindObjectOfType<WebSocketServer.WebSocketServer>().port;

            
            Debug.LogError($"test port {port}");
            WebSocketHelper.I.Connect($"ws://{address}:{port}");
        }

        if (GUILayout.Button("登 录"))
        {
            netManager.login(uid, (JsonData data) =>
            {
                if ((int)data["code"] == 0)
                {
                    log = "登录成功";
                }
                else
                {
                    log = (string)data["message"];
                }
            });
        }

        if (GUILayout.Button("创建房间"))
        {
            netManager.createRoom((JsonData data) =>
            {
                if ((int)data["code"] == 0)
                {
                    log = "创建房间,房间ID:" + (int)data["roomID"];
                    roomID = (int)data["roomID"] + "";
                }
                else
                {
                    log = (string)data["message"];
                }
            });
        }

        if (GUILayout.Button("加入房间"))
        {
            netManager.joinRoom(int.Parse(roomID), (JsonData data) =>
            {
                if ((int)data["code"] == 0)
                {
                    log = "加入房间成功";
                }
                else
                {
                    log = (string)data["message"];
                }
            });
        }

        if (GUILayout.Button("开始游戏"))
        {
            netManager.startGame((JsonData data) =>
            {
                log = "开始游戏" + data.ToJson();
            });
        }

        //if (GUI.Button(new Rect(180, 60, 120, 20), "获取我所在房间"))
        //{
        //    API.getMyRoom((JsonData data) =>
        //    {
        //        if (data["code"].asInt() == 0)
        //        {
        //            log = "获取我所在房间,房间ID:" + data["roomID"].asInt();
        //            roomID = data["roomID"].asInt() + "";
        //        }
        //        else
        //        {
        //            log = data["message"].asString();
        //        }

        //    });
        //}


        //if (GUI.Button(new Rect(320, 60, 120, 20), "获取0-20帧数据"))
        //{
        //    API.getFrames(0, 20, (JsonData data) =>
        //    {
        //        log = "获取0-20帧数据" + data.ToJson();
        //    });
        //}


        //List<int> ctrls = new List<int>();
        //if (GUI.Button(new Rect(420, 10, 80, 20), "发送操作 1"))
        //{
        //    ctrls.Clear();
        //    ctrls.Add(1);
        //    API.sendCtrl(ctrls);
        //}

        //if (GUI.Button(new Rect(520, 10, 80, 20), "发送操作 2"))
        //{
        //    ctrls.Clear();
        //    ctrls.Add(2);
        //    API.sendCtrl(ctrls);
        //}

        if (GUILayout.Button("强行断开"))
        {
            WebSocketHelper.I.CloseClientWebSocket();
        }
        GUILayout.EndHorizontal();


        GUILayout.BeginHorizontal();
        GUILayout.Label("用户ID:");
        GUILayout.Label(uid);
        GUILayout.EndHorizontal();

        GUILayout.BeginHorizontal();
        GUILayout.Label("房间ID:");
        GUILayout.Label(roomID);
        GUILayout.EndHorizontal();

        log2 = "";
        foreach (var entry in test.userBodies)
        {
            log2 += entry.Key + " | " + MyMathUtil.ToString(entry.Value.GetPosition()) + " | " + entry.Value.GetAngle() + "\n";
        }

        log2 += "remain frame count:  " + this.fuw.GetRemainFrameCount() + "\n";
        //log2 += "GetMsgDealTime():  " + this.fuw.GetMsgDealTime().ToString("F5") + "s\n";
        log2 += "msgSendDealTime:  " + msgSendDealTime.ToString("F5") + "s\n";
        log2 += "avgMsgSendDealTime:  " + (sumMsgSendDealTime / msgSendDealTimes.Length()).ToString("F5") + "s\n";
        log2 += "startTime:  " + (this.fuw.startTime).ToString("F5") + "s\n";

        var now = System.DateTime.Now;
        var diffNow = now.AddTicks(msgDuringEnqueueTime);
        var m2 = (diffNow - now).Milliseconds;
        log2 += "msgDuringEnqueueTime:  " + m2 + "ms\n";
        log2 += "joy stick times:  " + msgSentTimes.Count + "\n";
        log2 += "updateCountHerz: " + updateCountHerz + "\n";
        log2 += "frameMsgArr.Length: " + msgQueue.Length() + "\n";

        HashSet<long> msgSet = new HashSet<long>();
        foreach(var entry in msgSentTimes)
        {
            if(Time.realtimeSinceStartupAsDouble - entry.Value > 1)
            {
                msgSet.Add(entry.Key);
            }
        }

        foreach(var m in msgSet)
        {
            msgSentTimes.Remove(m);
        }

        GUILayout.Box(log + "\n" + log2);
    }
}
